/*
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

package com.xflib.framework.util;

import com.xflib.framework.commandline.CommandLineArgs;
import com.xflib.framework.commandline.CommandLineExitNonZeroException;
import com.xflib.framework.log.StreamGobbler;
import org.apache.commons.cli.*;
import org.apache.commons.exec.launcher.CommandLauncherFactory;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * CommandLine Util。
 *
 * @Author: koradji@zhou_zzq@163.com
 */
public class CommandLineUtil {

    private Options options;
    private String header="";
    private String footer="Please report issues to koradji@163.com";
    private boolean allowNonArgs=true;

    private CommandLineUtil(){}

    /**
     * 创建非必须的参数组
     * @return see {@link OptionGroup}
     */
    public static OptionGroup createOptionGroup(){return createOptionGroup(false);}

    /**
     * 创建参数组
     * @param required 是否必须
     * @return see {@link OptionGroup}
     */
    public static OptionGroup createOptionGroup(boolean required){
        OptionGroup og =  new OptionGroup();
        og.setRequired(required);
        return og;
    }

    /**
     * 静态构造函数
     * @param options  see {@link Options}
     * @return {@link CommandLineUtil}
     */
    public static CommandLineUtil create(Options options){
        CommandLineUtil commandLineUtil= new CommandLineUtil();
        commandLineUtil.options = options;
        return commandLineUtil;
    }

    /**
     * 设置header
     * @param header
     * @return {@link CommandLineUtil}
     */
    public CommandLineUtil setHeader(String header){
        this.header = header;
        return this;
    }

    /**
     * 设置 footer
     * @param footer
     * @return {@link CommandLineUtil}
     */
    public CommandLineUtil setFooter(String footer){
        this.footer = footer;
        return this;
    }

    /**
     * 解析桉树
     * @param args
     * @return see {@link CommandLine}
     */
    public CommandLine parseCommandLine(String... args) {
        CommandLineParser cliParser = new DefaultParser();

        CommandLine cli = null;
        try {
            if (args.length == 0) throw new ParseException(
                    I18nUtil.getOrDefault("ERROR_MISSING_ARGS","args was losing."));
            cli = cliParser.parse(options, args);
            if(cli.hasOption("h")){
                showHelper(options);
            }
        } catch (ParseException e) {
            // 解析失败是用 HelpFormatter 打印 帮助信息
            System.out.println(e.getMessage().concat("\n"));
            showHelper(options);
        }
        return cli;
    }

    /**
     * Show Help message.
     * @param options see {@link Options}
     */
    public void showHelper(Options options){
        this.showHelper(options, this.header, I18nUtil.getOrDefault("HELP_FOOTER",this.footer));
    }

    /**
     * Show Help message.
     * @param options see {@link Options}
     * @param header
     */
    public void showHelper(Options options, String header){
        this.showHelper(options, header, I18nUtil.getOrDefault("HELP_FOOTER",this.footer));
    }

    /**
     * Show Help message.
     * @param options see {@link Options}
     * @param header
     * @param footer
     */
    public void showHelper(Options options, String header, String footer){
        header = header + "\n\n";
        footer = "\n"+footer;
        HelpFormatter formatter = new HelpFormatter();
        formatter.setSyntaxPrefix(I18nUtil.getOrDefault("HELP_USAGE",formatter.getSyntaxPrefix()));
        formatter.printHelp("app", header, options, footer, true);
        System.exit(1);
    }

    /**
     * 执行 command line
     * @param commandLineArgs
     * @param debug
     * @param timeout
     * @param unit
     * @throws {@link IOException}, {@link InterruptedException}, {@link CommandLineExitNonZeroException}
     */
    public static int exec(
            CommandLineArgs commandLineArgs, boolean debug, long timeout, TimeUnit unit
    ) throws IOException, InterruptedException, CommandLineExitNonZeroException {
        int result=-1;

        Process process = CommandLauncherFactory.createVMLauncher().exec(
                commandLineArgs.getCmd(),
                commandLineArgs.getEnv(),
                commandLineArgs.getWorkDir()
        );

        try {
            // any error message?
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR");
            errorGobbler.setTimerOut(unit.toMillis(timeout)).setDeBug(true);
            // any output?
            StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT");
            outputGobbler.setTimerOut(unit.toMillis(timeout)).setDeBug(debug);

            // kick them off
            errorGobbler.start();
            outputGobbler.start();

            // any error ?
            result=process.waitFor();
        } finally {
            if (result != 0) {
                result = process.exitValue();
                throw new CommandLineExitNonZeroException(String.valueOf(result));
            }
            //process=null;
        }
        return result;
    }

}
